package multiconnector.broadcast;

import java.io.IOException;
import java.net.InetAddress;

import multiconnector.broadcast.ReceiverSocket.PacketListener;
import android.content.Context;
import android.content.Intent;
import android.util.Base64;
import android.util.Log;

/**
 * A {@link Broadcaster} with the functionality to send pings each x
 * milliseconds.
 */
public class Channel extends PingBroadcaster implements PacketListener {

	private static final String LOG_TAG = Channel.class.getName();
	protected static final String INTENT_EXTRA_SECURE_TAG = "__SECURED_TAG__";
	protected static final String INTENT_EXTRA_WAS_ENCRYPTED = "__WAS_ENCRYPTED_TAG__";
	protected static final String INTENT_EXTRA_SEND_ENCRYPTED = "__SEND_ENCRYPTED_TAG__";
	protected static final String INTENT_ACTION_SECURED = INTENT_ACTION
			+ "_SECURED";
	protected static final String GLOBAL = "--GLOBAL--";
	private String mChannelName = GLOBAL;
	private PacketListener mHandler;
	private String mPassphrase;

	public Channel(Context pContext, int pPort, String pApplicationTag) throws IOException {
		super(pContext, pPort, pApplicationTag);
		super.setOnReceiveHandler(this);
	}

	@Override
	protected Intent getSendIntent(Intent pIntent) {
		if (getChannel().equals(Channel.GLOBAL))
			return pIntent;
		// no passphrase set for this channel -> do not encrypt packet
		if (mPassphrase == null)
			return pIntent;
		// encrypt packet
		try {
			// not global channel -> encrypt intent
			pIntent.putExtra(INTENT_EXTRA_SECURE_TAG, mPassphrase);
			String uri = pIntent.toUri(0);
			byte[] data = new Encryption(mPassphrase).encrypt(uri.getBytes());
			Intent intent = new Intent(INTENT_ACTION_SECURED);
			intent.setAction(INTENT_ACTION_SECURED);
			intent.putExtra("data",
					new String(Base64.encode(data, Base64.DEFAULT)));
			return intent;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	protected static boolean wasEncrypted(Intent pIntent) {
		return pIntent.hasExtra(INTENT_EXTRA_WAS_ENCRYPTED);
	}

	@Override
	protected Intent getReceiveIntent(Intent pIntent) {
		// we dont have to decode this package
		if (!isPacket(pIntent, INTENT_ACTION_SECURED))
			return pIntent;
		// decrypt packet
		try {
			byte[] uri = Base64.decode(pIntent.getStringExtra("data")
					.getBytes(), Base64.DEFAULT);
			String data = new String(new Encryption(mPassphrase).decrypt(uri));
			Intent intent = Intent.parseUri(data, 0);
			if (!intent.hasExtra(INTENT_EXTRA_SECURE_TAG)) {
				// corrupted -> drop packet
				return null;
			}
			// wenn pw , und das intent ist nicht verschl��selt, dann droppen?
			if (!intent.getStringExtra(INTENT_EXTRA_SECURE_TAG).equals(
					mPassphrase)) {
				// corrupted -> drop packet
				return null;
			}
			// mark this intent that it was encrypted
			intent.putExtra(INTENT_EXTRA_WAS_ENCRYPTED, true);
			intent.removeExtra(INTENT_EXTRA_SECURE_TAG);
			return intent;
		} catch (Exception e) {
			// illegal passphrase -> drop packet
			return null;
		}
	}

	public void joinChannel(String pChannelName) {
		mChannelName = pChannelName;
	}

	public void joinChannel(String pChannelName, String pPassphrase) {
		setChannel(pChannelName);
		setPassphrase(pPassphrase);
	}

	public void leaveChannel() {
		mChannelName = GLOBAL;
	}

	protected void setChannel(String pChannelName) {
		mChannelName = pChannelName;
	}

	public void setPassphrase(String pPassphrase) {
		mPassphrase = pPassphrase;
	}

	public String getChannel() {
		return mChannelName;
	}

	@Override
	public void setOnReceiveHandler(PacketListener pHandler) {
		mHandler = pHandler;
	}

	public void send(Intent pIntent, int pPort, boolean pEncrypted) {
		pIntent.putExtra(INTENT_EXTRA_SEND_ENCRYPTED, pEncrypted);
		pIntent.putExtra("channelName", mChannelName);
		super.send(pIntent, pPort);
	}

	/**
	 * If a passphrase was set, the intent will be send encrypted, else it will
	 * be send without encryption.
	 */
	public void send(Intent pIntent, int pPort) {
		send(pIntent, pPort, mPassphrase != null);
	}

	/**
	 * If a passphrase was set, the intent will be send encrypted, else it will
	 * be send without encryption.
	 */
	@Override
	public void send(Intent pIntent) {
		send(pIntent, getPort(), mPassphrase != null);
	}

	@Override
	public void onPacketReceive(InetAddress pFrom, int pFromPort, Intent pData,
								boolean pWasEncrypted) {
		// not for the user's channel
		if (pData.hasExtra("channelName")
				&& !pData.getStringExtra("channelName").equals(mChannelName))
			return;

		if (mHandler != null)
			mHandler.onPacketReceive(pFrom, pFromPort, pData, pWasEncrypted);
	}

}
